package com.lagou.dubbo.filter;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Data;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * description: 性能监控
 *
 * @author ykl
 * @version V1.0
 * @date 2020/9/8 下午10:59
 */
@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {

    private static List<TpEntity> list = Collections.synchronizedList(Lists.newArrayList());

    private AtomicBoolean isPrint = new AtomicBoolean(false);
    ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
            new BasicThreadFactory.Builder().namingPattern("TPMonitor-schedule-pool").daemon(true).build());

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

        long startTime = System.currentTimeMillis();
        try {
            return invoker.invoke(invocation);
        } finally {
            long time = System.currentTimeMillis() - startTime;
            TpEntity tpEntity = new TpEntity(new Date(), time);
            list.add(tpEntity);

            if (this.isPrint.compareAndSet(false, true)) {

                executorService.scheduleAtFixedRate(() -> {

                    Date lastComputerTime = DateUtils.addSeconds(new Date(), -60);
                    if (!CollectionUtils.isEmpty(list)) {
                        List<TpEntity> tpEntities = list.stream().filter(entity -> {
                            if (entity.getDateTime() != null) {
                                return lastComputerTime.before(entity.getDateTime());
                            }
                            return false;
                        }).collect(Collectors.toList());
                        if (!CollectionUtils.isEmpty(tpEntities)) {
                            List<Long> optTimeList = tpEntities.stream().map(entity -> entity.getOptTime()).collect(Collectors.toList());
                            Collections.sort(optTimeList);
                            int count = optTimeList.size();
                            int tp90Index = new Double(Math.ceil(count * 0.9)).intValue() - 1;
                            int tp99Index = new Double(Math.ceil(count * 0.99)).intValue() - 1;

                            System.out.println("------TP90:" + optTimeList.get(tp90Index) + "----------");
                            System.out.println("------TP99:" + optTimeList.get(tp99Index) + "----------");
                        }
                    }
                }, 0, 5, TimeUnit.SECONDS);
            }
//            System.out.println("invoke time:"+time + "毫秒");


        }
    }


    public static void main(String[] args) {
        List<Long> longList = Lists.newArrayList();
        longList.add(2L);
        longList.add(1L);
        longList.add(3L);

        System.out.println(longList);

        Collections.sort(longList);
        System.out.println(longList);
        Collections.reverse(longList);
        System.out.println(longList);

        System.out.println(longList.get(3));


    }

    @Data
    class TpEntity {

        private Date dateTime;

        private long optTime;


        public TpEntity() {
        }

        public TpEntity(Date dateTime, long optTime) {
            this.dateTime = dateTime;
            this.optTime = optTime;
        }
    }
}

